package fr.irit.transf.refactoring.uml;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.UMLFactory;

public class GeneralisationProcess {
	
	private Package _current;
	
	public void run() {
		_current = (Package) ModelUtility.getModel("./model/source.uml").getMembers().get(0);
		transform(getClasses());
		//ModelUtility.save(_current, "./model/Target.uml");
	}

	private EList<Class> getClasses() {
		EList<NamedElement> namedelement = _current.getMembers();
		EList<Class> list = new BasicEList<Class>();
		for (NamedElement n: namedelement) {
			if (n instanceof Class){
				list.add((Class) n);
			}
		}
		return list;
	}
	
	public void transform(EList<Class> classes) {
		for (int i=0; i<classes.size(); i++) {
			for (int j=i+1; j<classes.size(); j++) {
				Class a = (Class) classes.get(i);
				Class b = (Class) classes.get(j);
				if (needRefactoring(a, b)) {
					_current.getOwnedTypes().add(extractSuperClass(a, b));
				}
			}
		}
		EObject tgt= EcoreUtil.copy(_current);
		ModelUtility.save(tgt, "./model/Target.uml");

//		System.out.println(tgt.eResource().getURI());
//		TreeIterator<EObject> tree=tgt.eAllContents();
//		
//		while (tree.hasNext())
//		{	EObject o=tree.next();
//			System.out.println(EcoreUtil.getURI(o));
//		}
		
	}
	
	public boolean needRefactoring(Class a, Class b) {
		for (Property aAttr : a.getOwnedAttributes()) {
			for (Property bAttr : b.getOwnedAttributes()) {
				return equalsAttributes(aAttr,bAttr);
			}
		}
		return false;
	}
	
	private boolean equalsAttributes(Property a, Property b) {
		return a.getName().equals(b.getName()) && a.getType().equals(b.getType());
	}

	public Class extractSuperClass (Class a, Class b) {
		Class result = UMLFactory.eINSTANCE.createClass();
		result.setName(a.getName() + " ^ " + b.getName());
		EList<Property> aAttribute = a.getOwnedAttributes();
		EList<Property> bAttribute = b.getOwnedAttributes();
		EList<Couple<Property>> toFactorize = new BasicEList<Couple<Property>>(); 
		for (Property aAttr : aAttribute) {
			for (Property bAttr : bAttribute) {
				if (equalsAttributes(aAttr,bAttr)) {
					toFactorize.add(new Couple<Property>(aAttr, bAttr));
				}
			}
		}
		for (Couple<Property> c : toFactorize) {
			pullUpField(c.getFirst(), c.getSecond(), result);
		}
		result.setIsAbstract(true);
		addInheritanceLink(a, result);
		addInheritanceLink(b, result);
		return result;
	}
	
	public Property pullUpField(final Property a1, final Property a2, Class target) {
		Property result = (Property) clone(a1);
		a1.getClass_().getOwnedAttributes().remove(a1);
		a2.getClass_().getOwnedAttributes().remove(a2);
		target.getOwnedAttributes().add(result);
		return result;
	}

	private Property clone(Property eo) {
		return (Property) EcoreUtil.copy(eo);
	}

	public void addInheritanceLink(Class subClass,final Class superClass) {
		Generalization g = UMLFactory.eINSTANCE.createGeneralization();
		g.setGeneral(superClass);
		g.setSpecific(subClass);
		subClass.getGeneralizations().add(g);
	}

	public static void main(String[] args){
		
		GeneralisationProcess gp= new GeneralisationProcess();
		gp.run();
	}
	
	private class Couple<T> {

		T first;
		T second;
		
		public T getFirst() {
			return first;
		}

		public T getSecond() {
			return second;
		}

		public Couple(T f, T s) {
			first = f;
			second = s;
		}
	}
}
